﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AGKWrapperAid
{
    public class FunctionParameter
    {
        private string name;
        public string Type;
        public bool IsDefault;

        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                name = name.Replace("*","");
                name = name.Replace("&","");
            }
        }
    }

    public class FunctionCall
    {
        public List<FunctionParameter> Parameters = new List<FunctionParameter>();
        public string ReturnType = null;
        public string Name;

        public string Write()
        {
            string output = "static ";

            if (ReturnType != null)
                output += ReturnType + " ";
            else
                output += "void ";

            output += Name + "(";

            if (Parameters.Count > 0)
            {
                for (int i = 0; i < Parameters.Count; i++)
                {
                    output += Parameters[i].Type + " " + Parameters[i].Name;
                    if (i < Parameters.Count - 1)
                        output += ",";
                }
            }

            output += ") { " + WriteCall() + "}";

            return output;
        }

        string WriteCall()
        {
            string output = "";
            foreach (var p in Parameters)
            {
                if (p.Type == "String^")
                {
                    output += "PARAMSTRING_START(c_" + p.Name + "," + p.Name + ") ";
                }
                if (p.Type == "uString^")
                {
                    output += "PARAMSTRING_START(c_" + p.Name + "," + p.Name + ") ";
                }
            }

            if (ReturnType != null)
            {
                if (ReturnType == "String^")
                    output += "System::String^ r = gcnew System::String(reinterpret_cast<const char*>(agk::";
                else
                    output += ReturnType + " r = agk::";
            }
            else
                output += "agk::"; 
            output += Name + "(";

            if (Parameters.Count > 0)
            {
                for(int i=0;i<Parameters.Count;i++)
                {
                    if (Parameters[i].Type == "String^")
                        output += "c_";
                    output += Parameters[i].Name;
                    if (i < Parameters.Count - 1)
                        output += ",";
                }
            }

            if (ReturnType == "String^")
                output += ")));";
            else
                output += "); ";

            foreach (var p in Parameters)
            {
                if (p.Type == "String^")
                {
                    output += "PARAMSTRING_END(c_" + p.Name + ") ";
                }
            }

            if (ReturnType != null)
            {
                output += "return r;"; 
            }

            return output;
        }

    }

    public class Translator
    {
        public string Translate(List<Token> tokens)
        {
            int i = 0;
            Token t;
            FunctionCall f = new FunctionCall();

            // first we should have a static qualifier
            t = tokens[i++];
            if (t.Symbol == "static")
            {
                t = tokens[i++];

                // return type
                if (t.Type == TokenType.Literal)
                {
                    f.ReturnType = TranslateType(t.Symbol);

                    // function name
                    t = tokens[i++];
                    if (t.Type != TokenType.Literal)
                        throw new Exception("expected function name: " + t.Symbol);
                    f.Name = t.Symbol;

                    // function parameters
                    t = tokens[i++];
                    if (t.Type == TokenType.ParameterStart)
                    {
                        t = tokens[i++];
                        while (true)
                        {
                            if (t.Type == TokenType.Literal)
                            {
                                var p = new FunctionParameter();
                                p.Type = TranslateType(t.Symbol);
                                t = tokens[i++];
                                if (t.Type != TokenType.Literal)
                                    throw new Exception("expected parameter name: " + t.Type.ToString());
                                p.Name = t.Symbol;
                                t = tokens[i++];
                                if (t.Type == TokenType.ParameterEqual)
                                {
                                    t = tokens[i++]; // get number
                                    t = tokens[i++];
                                    p.IsDefault = true;
                                }
                                if (t.Type == TokenType.ParameterSep)
                                    t = tokens[i++];
                                f.Parameters.Add(p);
                            }
                            else if (t.Type == TokenType.ParameterEnd)
                                break;
                            else
                                throw new Exception("expected parameter type: " + t.Type.ToString());
                        }
                    }
                    else
                        throw new Exception("expected parameter list: " + t.Type.ToString() + "," + t.Symbol);
                }
                else
                    throw new Exception("token not literal after static: " + t.Type.ToString());
            }
            else
                throw new Exception("first token is not 'static': " + tokens[0].Symbol);

            return f.Write();
        }

        string TranslateType(string input)
        {
            switch (input)
            {
                case "UINT":
                    return "UInt32";
                case "void":
                    return null;
                case "int":
                    return "int";
                case "char*":
                    return "String^";
                case "char":
                    return "String^";
                case "float":
                    return "float";
                default:
                    throw new Exception("unrecognized return type: " + input);
            }
        }
    }
}
